package org.alvin.ocpp.server;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import eu.chargetime.ocpp.*;
import eu.chargetime.ocpp.feature.profile.ServerCoreEventHandler;
import eu.chargetime.ocpp.feature.profile.ServerCoreProfile;
import eu.chargetime.ocpp.model.Request;
import eu.chargetime.ocpp.model.SessionInformation;
import eu.chargetime.ocpp.model.core.*;
import org.alvin.mini_inject.annotations.MiniComponent;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.lang.reflect.InvocationTargetException;
import java.security.KeyStore;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Set;
import java.util.UUID;

@MiniComponent
public class OCPP1_6JSONServer extends OcppJsonServer {

    private JSONServer server;
    private ServerCoreProfile core;

    //    private String hostIp = "192.168.1.97";
//    private Integer port = 8889;
    private boolean wssEnabled = false;
    private String storeType = "JKS";
    private String keyStore = "/xlocpp_2034_04_30.jks";
    private String storePassword = "xlocpp123456";
    private String keyPassword = "xlocpp123456";

//    keytool -genkeypair -alias xlocpp_2034_04_30 -keyalg RSA -keysize 2048 -keystore xlocpp_2034_04_30.jks -validity 3650
    // 姓氏 localhost
    // xlocpp123456

    //  keytool -genkeypair -alias xl.ocpp.com -keyalg RSA -keysize 2048 -keystore xlocpp_2034_04_30.jks -validity 3650

    //  keytool -genkey -alias localhost -keyalg RSA -keysize 2048 -keystore localhost.keystore.jks -validity 3650

    private static final String OCPP_DEVICE_KEY = "OCPP_Device_Key_16J:";

    private Cache<String, OcppDevice> redisService = CacheBuilder.newBuilder().build();

    private SimuatorService simuatorService = new SimuatorService();

    public void stop() {
        if (server != null && !server.isClosed()) {
            server.close();
        }
    }

    public void start(String ip, int port) throws Exception {
        // The core profile is mandatory
        core = new ServerCoreProfile(new ServerCoreEventHandler() {
            @Override
            public AuthorizeConfirmation handleAuthorizeRequest(UUID sessionIndex, AuthorizeRequest request) {
                OCPP1_6JSONServer.this.logConsole("handleAuthorizeRequest：{}---{}:", sessionIndex, request);
                //拿到信息
                String key = OCPP_DEVICE_KEY.concat(sessionIndex.toString());
//                if (redisService.getIfPresent(key) != null) {
//                    IdTagInfo tagInfo = new IdTagInfo();
//                    tagInfo.setParentIdTag(request.getIdTag());
//                    tagInfo.setStatus(AuthorizationStatus.Invalid);
//                    return new AuthorizeConfirmation(tagInfo);
//                }
                IdTagInfo tagInfo = new IdTagInfo();
                tagInfo.setParentIdTag(request.getIdTag());
                tagInfo.setStatus(AuthorizationStatus.Accepted);
                tagInfo.setExpiryDate(ZonedDateTime.now().plus(Period.ofDays(3)));
                return new AuthorizeConfirmation(tagInfo);
            }

            @Override
            public BootNotificationConfirmation handleBootNotificationRequest(UUID sessionIndex, BootNotificationRequest request) {
                OCPP1_6JSONServer.this.logConsole("handleBootNotificationRequest：{}---{}:", sessionIndex, request);
                //拿到信息
//                String key = OCPP_DEVICE_KEY.concat(sessionIndex.toString());
//                if (redisService.getIfPresent(key) != null) {
//                    BootNotificationConfirmation bootNotificationConfirmation = new BootNotificationConfirmation();
//                    bootNotificationConfirmation.setCurrentTime(ZonedDateTime.now());
//                    bootNotificationConfirmation.setInterval(30);
//                    bootNotificationConfirmation.setStatus(RegistrationStatus.Rejected);
//                    return bootNotificationConfirmation;
//                }
                //判断是否可以启动
                BootNotificationConfirmation bootNotificationConfirmation = new BootNotificationConfirmation();
                bootNotificationConfirmation.setCurrentTime(ZonedDateTime.now());
                bootNotificationConfirmation.setInterval(30);
                bootNotificationConfirmation.setStatus(RegistrationStatus.Accepted);
                return bootNotificationConfirmation;
            }

            @Override
            public DataTransferConfirmation handleDataTransferRequest(UUID sessionIndex, DataTransferRequest request) {
                OCPP1_6JSONServer.this.logConsole("handleDataTransferRequest:{}", request);
                DataTransferConfirmation dataTransferConfirmation = new DataTransferConfirmation();
                return dataTransferConfirmation; // returning null means unsupported feature
            }

            @Override
            public HeartbeatConfirmation handleHeartbeatRequest(UUID sessionIndex, HeartbeatRequest request) {
                OCPP1_6JSONServer.this.logConsole("-------ocpp1.6-json-- 进入心跳----{}", request);
                HeartbeatConfirmation heartbeatConfirmation = new HeartbeatConfirmation();
                heartbeatConfirmation.setCurrentTime(ZonedDateTime.now());
                OCPP1_6JSONServer.this.logConsole("-------ocpp1.6-json--心跳响应---------{}", heartbeatConfirmation);
                return heartbeatConfirmation; // returning null means unsupported feature
            }

            @Override
            public MeterValuesConfirmation handleMeterValuesRequest(UUID sessionIndex, MeterValuesRequest request) {
                OCPP1_6JSONServer.this.logConsole("handleMeterValuesRequest:{}", request);
                MeterValuesConfirmation meterValuesConfirmation = new MeterValuesConfirmation();
                return meterValuesConfirmation; // returning null means unsupported feature
            }

            @Override
            public StartTransactionConfirmation handleStartTransactionRequest(UUID sessionIndex, StartTransactionRequest request) {
                OCPP1_6JSONServer.this.logConsole("handleStartTransactionRequest:{}", request);
                StartTransactionConfirmation startTransactionConfirmation = new StartTransactionConfirmation();
                startTransactionConfirmation.setTransactionId(24361);

                IdTagInfo idTagInfo = new IdTagInfo();
                idTagInfo.setStatus(AuthorizationStatus.Accepted);
                idTagInfo.setParentIdTag(request.getIdTag());
                LocalDateTime time = ChronoUnit.YEARS.addTo(LocalDateTime.now(), 1);
                idTagInfo.setExpiryDate(time.atZone(ZoneId.systemDefault()));

                startTransactionConfirmation.setIdTagInfo(idTagInfo);
//                {"transactionId":24361,"idTagInfo":{"expiryDate":"2024-07-04T16:10:45.577+08:00","parentIdTag":"ozge_4M","status":"Accepted"}}
                return startTransactionConfirmation; // returning null means unsupported feature
            }

            @Override
            public StatusNotificationConfirmation handleStatusNotificationRequest(UUID sessionIndex, StatusNotificationRequest request) {
                //改变枪状态
                OCPP1_6JSONServer.this.logConsole("handleStatusNotificationRequest:{}", request);
                StatusNotificationConfirmation statusNotificationConfirmation = new StatusNotificationConfirmation();
                return statusNotificationConfirmation; // returning null means unsupported feature
            }

            @Override
            public StopTransactionConfirmation handleStopTransactionRequest(UUID sessionIndex, StopTransactionRequest request) {
                OCPP1_6JSONServer.this.logConsole("handleStopTransactionRequest:{}", request);
                StopTransactionConfirmation stopTransactionConfirmation = new StopTransactionConfirmation();
                IdTagInfo idTagInfo = new IdTagInfo();
                idTagInfo.setStatus(AuthorizationStatus.Accepted);
                idTagInfo.setParentIdTag(request.getIdTag());
                LocalDateTime time = ChronoUnit.YEARS.addTo(LocalDateTime.now(), 1);
                idTagInfo.setExpiryDate(time.atZone(ZoneId.systemDefault()));

                stopTransactionConfirmation.setIdTagInfo(idTagInfo);
                return stopTransactionConfirmation; // returning null means unsupported feature
            }
        });

        server = new JSONServer(core);
        if (this.wssEnabled) {
            KeyStore ks = KeyStore.getInstance(storeType);
            ks.load(this.getClass().getResourceAsStream(keyStore), storePassword.toCharArray());
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(ks, keyPassword.toCharArray());
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
            tmf.init(ks);

            SSLContext sslContext = SSLContext.getInstance("TLS"); // TLS
            sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
            server.enableWSS(sslContext);
        }
        server.open(ip, port, new ServerEvents() {
            @Override
            public void newSession(UUID sessionIndex, SessionInformation information) {
                // sessionIndex is used to send messages.
                OCPP1_6JSONServer.this.firePropertyChange("online", information, sessionIndex);
                OCPP1_6JSONServer.this.logConsole("----ocpp1.6-json-- 设备连入 " + sessionIndex + ": " + information.getIdentifier() + "---||---" + information.getAddress());
                OcppDevice device = new OcppDevice();
                device.setSessionIndex(sessionIndex.toString());
                device.setIdentifier(information.getIdentifier());
                device.setVersion("1.6J");
                //放入redis
                String key = OCPP_DEVICE_KEY.concat(sessionIndex.toString());
                redisService.put(key, device);
            }

            @Override
            public void lostSession(UUID sessionIndex) {
                OCPP1_6JSONServer.this.firePropertyChange("offline", true, sessionIndex);
                OCPP1_6JSONServer.this.logConsole("----ocpp1.6-json--设备离线 " + sessionIndex + " lost connection");
                String key = OCPP_DEVICE_KEY.concat(sessionIndex.toString());
                //从redis 中删除
                redisService.invalidate(key);
            }
        });
    }

    public void sendCmd(String sessionIndex, String cmd) throws OccurenceConstraintException, UnsupportedFeatureException, NotConnectedException, InvocationTargetException, IllegalAccessException {
        System.out.println(sessionIndex + ":  " + cmd);
        Request request = this.simuatorService.createDefaultRequest(core, cmd);
        if (request == null) {
            OCPP1_6JSONServer.this.logConsole("未发送：{} == {}", sessionIndex, cmd);
            return;
        }
        server.send(UUID.fromString(sessionIndex), request).whenComplete((confirmation, throwable) -> OCPP1_6JSONServer.this.logConsole("whenComplete:{}", confirmation));
    }


    public Set<String> getCmds(){
        return this.simuatorService.getCmds();
    }


}

